#!/usr/bin/env python
# Author: Chris Moyer
VERSION = 0.1

import os
import sys
sys.path.append(".")
sys.path.append("lib")

import marajo
marajo.set_env(".", None)

from datetime import datetime, timedelta


def choice_input(cls, default=None, title=None, filters=[]):
    """
    Choice input
    """
    if title == None:
        title = "Please choose a %s" % cls.__name__.lower()
    print title
    objects = []
    query = cls.find()
    for filter in filters:
        query.filter(filter[0], filter[1])
    for n, obj in enumerate(query):
        print "%s: %s" % (n, obj)
        objects.append(obj)
    choice = raw_input("%s: " % cls.__name__.lower())
    try:
        choice = objects[int(choice)]
    except:
        choice = default
    return choice

def ticket_report(query):
    """
    Base report generator for ticket
    based reports
    """
    from resources.project import Project
    from resources.release import Release
    from resources.ticket import Ticket
    from resources.client import Client

    print "Please choose a start date (optional)"
    start_date = raw_input("start_date (mm/dd/yy): ").strip()
    if start_date:
        start_date = datetime.strptime(start_date, "%m/%d/%y")
        query.filter("date_finished >", start_date)

    print "Please choose an end date (optional)"
    end_date = raw_input("end_date (mm/dd/yy): ").strip()
    if end_date:
        end_date = datetime.strptime(end_date, "%m/%d/%y")
        query.filter("date_finished <", end_date)
    else:
        end_date = datetime.now()

    
    # Prompt for rates
    print "Please specify hourly rates:"
    rates = {}
    for rate in Ticket.find_property("rate").choices:
        rates[rate] = int(raw_input("%s: " % rate))

    # Prompt for Contact info
    client = choice_input(Client, title="Please choose the client to bill:")

    tickets = []
    total_minutes = 0
    total_cost = 0
    total_estimated_hours = 0
    total_estimated_cost = 0
    for t in query:
        total_minutes += t.minutes
        total_cost += ((float(t.minutes)/60) * rates[t.rate])
        total_estimated_hours += t.estimated_hours
        total_estimated_cost += (t.estimated_hours * rates[t.rate])
        tickets.append(t)

    return {"project": project, 
                "tickets": tickets, 
                "total_hours": float(total_minutes)/60, 
                "total_cost": total_cost, 
                "total_estimated_hours": total_estimated_hours,
                "total_estimated_cost": total_estimated_cost,
                "rates": rates,
                "client": client,
                "date": datetime.now(),
                "date_due": datetime.now() + timedelta(days=30),
                "start_date": start_date,
                "end_date": end_date,
                "release": release,
                }

def query_range():
    """
    Ask the user for a start date and end date (both optional)
    """
    print "Please choose a start date (optional)"
    start_date = raw_input("start_date (mm/dd/yy): ").strip()
    if start_date:
        start_date = datetime.strptime(start_date, "%m/%d/%y")
    else:
        start_date = None

    print "Please choose an end date (optional)"
    end_date = raw_input("end_date (mm/dd/yy): ").strip()
    if end_date:
        end_date = datetime.strptime(end_date, "%m/%d/%y")
    else:
        end_date = datetime.now()
    return (start_date, end_date)


def comment_report(query):
    """
    Base for comment reports
    """
    from resources.ticket import Ticket
    total_minutes = 0
    total_cost = 0
    projects = {}
    comments = []
    clients = {}
    for comment in query:
        total_minutes += comment.minutes
        comment.ticket = Ticket.get_by_id(comment.ticket.id)
        if not projects.has_key(comment.ticket.project.id):
            projects[comment.ticket.project.id] = {
                "project": comment.ticket.project,
                "minutes": 0,
                "comments": []
            }
        for client in comment.ticket.clients:
            if not clients.has_key(client.id):
                clients[client.id] = {
                    'name': client.name,
                    'minutes': 0,
                }

            clients[client.id]['minutes'] += comment.minutes
        projects[comment.ticket.project.id]['minutes'] += comment.minutes
        projects[comment.ticket.project.id]['comments'].append(comment)
        comments.append(comment)
    vals = {}
    vals['comments'] = comments
    vals['clients'] = clients
    vals['projects'] = projects
    vals['total_minutes'] = total_minutes
    return vals

def work_report():
    from marajo.appengine.api.users import User
    from resources.comment import Comment
    query = Comment.find()
    query.filter("minutes != ", 0)
    user = choice_input(User)
    query.filter("owner =", user)

    (start_date, end_date) = query_range()
    if start_date:
        query.filter("date >", start_date)
    if end_date:
        query.filter("date <", end_date)

    vals = comment_report(query)
    vals['user'] = user
    vals['start_date'] = start_date
    vals['end_date'] = end_date
    return vals

def invoice_report():
    from resources.project import Project
    from resources.comment import Comment
    from resources.client import Client
    query = Comment.find()
    query.filter("minutes != ", 0)

    (start_date, end_date) = query_range()
    if start_date:
        query.filter("date >", start_date)
    if end_date:
        query.filter("date <", end_date)


    print "Generating reports..."
    vals = comment_report(query)
    vals['start_date'] = start_date
    vals['end_date'] = end_date
    return vals

def estimate_report():
    from resources.ticket import Ticket
    query = Ticket.find()
    query.filter("status =", "open")
    return ticket_report(query)

##
# Output Generators
##
def generate_html(template, vals):
    out_f_name = "report.html"
    f = open(out_f_name, "w")
    f.write(template.render(report_type['template'],
                vals,
                template_dirs=["templates/reports"]
                ))
    f.close()

def generate_xls(report_type, vals):
    """
    Generate an Excell document
    """
    print "Generating XLS"
    if report_type == "W":
        return generate_xls_work_report(vals)
    elif report_type == "I":
        return generate_xls_invoice(vals)

def generate_xls_invoice(vals):
    """
    Generate an excel version of this invoice
    """

def generate_xls_work_report(vals):
    """
    Generate the work report XLS
    """
    import xlwt
    w = xlwt.Workbook()

    header_font = xlwt.Font()
    header_font.name = 'Times New Roman'
    header_font.bold = True

    header_borders = xlwt.Borders()
    header_borders.bottom = 2

    header_style = xlwt.XFStyle()
    header_style.font = header_font
    header_style.borders = header_borders

    totals_style = xlwt.XFStyle()
    totals_style.font = header_font
    totals_style.borders = xlwt.Borders()
    totals_style.borders.top = 2

    projects = vals['projects']
    for project in projects.values():
        sheet = w.add_sheet(project['project'].name)
        # Header Row
        sheet.write(0,0, "Date", header_style)
        sheet.col(0).width = 0x1200

        sheet.write(0,1, "Component", header_style)
        sheet.col(1).width = 0x1200

        sheet.write(0,2, "Details", header_style)
        sheet.col(2).width = 0x4000

        sheet.write(0,3, "Type", header_style)
        sheet.col(3).width = 0x1200

        sheet.write(0,4, "Hours", header_style)
        sheet.col(4).width = 0x0600

        row = 0
        for comment in project['comments']:
            row += 1
            sheet.write(row, 0, comment.date.strftime("%d %b %Y"))
            sheet.write(row, 1, str(comment.ticket.component))
            sheet.write(row, 2, str(comment.ticket.name))
            sheet.write(row, 3, str(comment.ticket.type))
            sheet.write(row, 4, float(comment.minutes)/60)
        row += 1
        sheet.write(row, 3, "Total", totals_style)
        sheet.write(row, 4, float(project['minutes'])/60, totals_style)

    # Add the summary report
    sheet = w.add_sheet("Summary")
    row = 0
    sheet.write(0,0, "Project", header_style)
    sheet.col(0).width = 0x4000
    sheet.write(0,1, "Hours", header_style)
    sheet.col(1).width = 0x0600

    for project in projects.values():
        row += 1
        sheet.write(row, 0, project['project'].name)
        sheet.write(row, 1, float(project['minutes'])/60)
    row += 1
    sheet.write(row, 0, "Total", totals_style)
    sheet.write(row, 1, float(vals['total_minutes'])/60, totals_style)
    w.save("report.xls")




report_types = {
    "I":  {
                "name": "Invoice", 
                "template": "invoice.tmpl", 
                "generator": invoice_report
            },
    "E":  {
                "name": "Estimate",
                "template": "estimate.tmpl",
                "generator": estimate_report
            },
    "W":  {
                "name": "Work Report",
                "template": "work_report.tmpl",
                "generator": work_report,
            }
}

if __name__ == "__main__":
    from optparse import OptionParser
    parser = OptionParser(version = "%prog version 1.0")
    parser.add_option("-e", "--environment", help="Environment", dest="env", default="prod")
    (options, args) = parser.parse_args()

    import marajo
    marajo_dir = os.path.dirname(os.path.dirname( os.path.realpath( __file__ ) ))
    marajo.set_env(marajo_dir, options.env)

    from marajo.appengine.ext.webapp import template
    print "Please choose a Report Type:"
    for k in report_types:
        print "%s: %s" % (k, report_types[k]['name'])

    report_name = raw_input("type: ").upper()
    report_type = report_types[report_name]

    vals = report_type['generator']()
    generate_html(template, vals)
    generate_xls(report_name, vals)
